<?php
/**
 *
 *
 * @author Zeon <scplay@gmail.com>
 * @date 2018/6/26 14:45
 * @copyright PanxSoft Inc.
 */

namespace App\Services\Repositories\Device;


use App\Exceptions\ApiErrCode;
use App\Models\Customer\DeviceAccount;
use App\Models\Panx\Product;
use App\Models\Panx\RecordType;
use App\Services\BaiduGeoService\MapApi;
use App\Services\Utils\Guard;
use App\Services\Utils\Transaction;
use Carbon\Carbon;
use Illuminate\Redis\Connections\PredisConnection;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\DB;

class DeviceAccountRepository
{
    const UPDATE_ACTIVE_THRESHOLD_SECOND = 60; // 1min 更新一次，在线时长没必要太精确

    /**
     * @param Product $product
     * @param int $increment
     * @return mixed
     * @throws \Exception
     */
    public function recordsProductTryOn(Product $product, $increment = 1)
    {
        $device_account = Guard::deviceAccount();

        $save_data = $this->assembleTryOnData($device_account, $product, $increment);

        $create = Transaction::handle(function () use ($product, $save_data, $increment) {

            $product->increment('try_on', $increment);

            $create = DB::table('records')->insert($save_data);

            return $create;
        });

        return $create;
    }

    private function assembleTryOnData(DeviceAccount $device_account,
                                       Product $product,
                                       int $increment)
    {
        $data = [];
        for ($count = 1; $count <= $increment; $count++) {
            $data[] = [
                'recorderable_id'   => $device_account->id,
                'recorderable_type' => class_basename(DeviceAccount::class),
                'objectable_id'     => $product->id,
                'objectable_type'   => class_basename(Product::class),
                'record_type_id'    => RecordType::PRODUCT_TRYON,
                'description'       => '商品试戴',
                'user_agent'        => $_SERVER['HTTP_USER_AGENT'] ?? '',
                'created_at'        => Carbon::now(),
                'updated_at'        => Carbon::now(),
            ];
        }
        return $data;
    }


    /**
     * 更新设备登录保存的信息
     *
     * @test
     *
     * @param DeviceAccount $device_account
     * @param array $request_data
     * @param string $token
     *
     * @return mixed
     * @throws \GuzzleHttp\Exception\GuzzleException
     */
    public function updateLoginInfo(DeviceAccount $device_account,
                                    array $request_data, string $token): bool
    {
        $update_data = [
            'last_login_at'  => Carbon::now(),
            'last_active_at' => Carbon::now(),
            'alive_seconds'  => $device_account->alive_seconds + 1, // 更新在线时长
            'remember_token' => $token, // 保存token
        ];

        $location = $this->tryGetLocationFromLogin($device_account, $request_data);
        if ($location) {
            $update_data['location'] = $location;
        }

        $updated = $device_account->update($request_data + $update_data);

        return $updated;
    }


    /**
     * @param DeviceAccount $device_account
     * @return string
     * @throws \GuzzleHttp\Exception\GuzzleException
     */
    private function getLocation(DeviceAccount $device_account): string
    {
        $res = (new MapApi)->geoCodeReverseQuery(
            (float) $device_account->latitude,
            (float) $device_account->longitude
        );

        $location = $res['result']["formatted_address"] ?? '';

        return $location;
    }

    /**
     * 记录累积在线时长，在中间件AppAuth中处理
     *
     * @test
     *
     * @param DeviceAccount $device_account
     * @return mixed
     * @throws \Exception
     */
    public function updateUseTime(DeviceAccount $device_account): bool
    {
        $now = now();
        $elapse_seconds = $now->diffInSeconds($device_account->last_active_at);
        if ($elapse_seconds < self::UPDATE_ACTIVE_THRESHOLD_SECOND) {
            return false;
        }

        /**
         * 首次登录的设备，活动时间更新为当前，在线时长 +1s
         *
         * @since php 5.2.2
         * @link http://php.net/manual/en/class.datetime.php - Changelog 关于 DateTime 可使用比较操作符
         */
        $start_of_2018 = Carbon::create(2018, 1, 1);
        if ($device_account->last_active_at < $start_of_2018) {
            $elapse_seconds = 1;
        }

        $update = $device_account->update([
            'alive_seconds' => $device_account->alive_seconds + $elapse_seconds,
            'last_active_at' => $now
        ]);

        return $update;
    }

    public function saveUseTimeToCache(DeviceAccount $device_account)
    {
        $seconds = now()->diffInSeconds($device_account->last_active_at);

        // 防止超过 int 最大值, 最大值需要在线 13 年，应该没有检查的必要
        if ($device_account->alive_seconds + $seconds >= DeviceAccount::ALIVE_SECONDS_MAX) {
            $seconds = DeviceAccount::ALIVE_SECONDS_MAX;
        } else {
            $seconds = $device_account->alive_seconds + $seconds;
        }

        $hash = 'beauty_mirror:device_account_alive_seconds'; // 每访问一次就更新 device_account_alive_seconds 表的 value 值
        $key = 'device_account_alive_seconds_' . $device_account->id;
        $value = $seconds;

        // redis 用法
        /** @var PredisConnection $redis */
       /* $redis = app('redis.connection');

        $redis->hset($hash, $key, $value); //将key为'key1' value为'v1'的元素存入hash1表*/

//        $redis->hmget('hash1',array('key3','key4')); //返回相应的值 array('v3','v4')

//        $x = $redis->hget($hash, $key);  //取出表'hash1'中的key 'key1'的值,返回'v1'

        // 缓存用法
        $cache = Cache::rememberForever($key, function() use ($key ,$value) {
            return $value;
        });


        return true;
        
    }

    public function getUseTimeForCache()
    {
        /** @var PredisConnection $redis */
        $redis = app('redis.connection');

        $all = $redis->hgetall('beauty_mirror:device_account_alive_seconds');

        return $all;
    }

    /**
     * 尝试更新地理位置
     *
     * @param DeviceAccount $device_account
     *
     * @param array $geo_data
     *
     * @return string
     * @throws \GuzzleHttp\Exception\GuzzleException
     * @throws \Exception
     */
    private function tryGetLocationFromLogin(DeviceAccount $device_account, array $geo_data): ?string
    {
        if (! $this->isDataHasGeoInfo($geo_data)) {
            return null;
        }

        // 登录这时数据要手动写进属性
        $device_account->latitude = $geo_data['latitude'];
        $device_account->longitude = $geo_data['longitude'];

        try {
            return $this->getLocation($device_account);
        } catch (\Exception $e) {
            if (config('app.debug')) throw $e;

            logger(ApiErrCode::LOG_PREFIX . $e->getMessage());

            return null;
        }
    }

    /**
     * @param array $request_data
     *
     * @return bool
     */
    private function isDataHasGeoInfo(array $request_data): bool
    {
        return isset($request_data['latitude'], $request_data['longitude']);
    }
}